#include <string.h>
#include <windows.h>

#include "lib.h"
#include "patch.h"
#include "listpkg.h"
#include "client.h"
#include "patchsrv.h"
#include "pkglist.h"
#include "pkgdesc.h"

// Send list of available packages  (send-only protocol)
//
// Request:
//
// <2><L>
//
// Responses to this request are:
//
// <3><E><initial error>
//
//	or
//
// <4><S><proto ver><# pkgs>
//		<pkg>..<pkg>
//			or
//		<3><E><error after initial success>
//
// Descriptions:
//
// <initial error>
//		EM_ServerDownForMaintenance:  generated in DetermineRequest
//
//	<pkg>:  each encoded pkg description as follows:
//		<msg len byte><L><pkgname len byte><pkgname bytes><4byte proto ver><4byte zipfile len>
//
//	<error after initial success>
//		EM_PkgListChanged:  	if # packages requires multiple writes and the
//								list changes between writes
//
// No other errors are possible.
//

ListPackages::ListPackages(Client* client)
	: Protocol(client)
{
	tosend = 0;
	done = true;
	senddesc = false;
    m_pkgListChangeCount = 0;
}

bool8
ListPackages::recognize(uint8* Msg, uint32 Len)
{
	bool8 success;

	m_multibyte = false;
	success = (bool8)(Msg[0] == 'L' && Len >= 1);
	if (!success)
	{
		success = (bool8)(((*(uint16*)&Msg[0]) == *(uint16*)"ML")&& Len >= 2);
		if (success)
			m_multibyte = true;
	}
	return success;
}

int32
ListPackages::start(uint8* Msg, uint32 Len)
{
	if (Len == 1 || (Len==2 && m_multibyte))
	{
		tosend = 0;
		done = false;
		senddesc = false;
		m_pkgListChangeCount = PatchServer::This->getPkgListChangeCount();
		return EM_Success;
	}
	else
	{
		PackageList* list;
		PackageDescription* item;
//		char pkgname[128];
		String pkgname;
		
		if (m_multibyte)
		{
			pkgname.deserialize(&Msg[2]);
		}
		else
		{
			char buf[128];
			strncpy(buf, (char*)&Msg[1], Len-1);
			buf[Len-1] = '\0';
			pkgname = buf;
		}
		
		list = PatchServer::This->getPackageList();
		if (!list)
		{
			done = true;
			return EM_PkgListChanged;
		}

		item = list->getItem(tosend);

		while (item)
		{
			if (item->getName().caseCompare(pkgname) == 0)
			{
				if (!item->send(client))
					item = 0;
				break;
			}

			item = item->getNext();
		}

		PatchServer::This->releasePackageList();

		if (!item)
			return EM_UnknownPackage;

		done = true;
		senddesc = true;

		return EM_Success;
	}
}

void
ListPackages::netIn(int32 /*Amt*/)
{
	// start protocol...ListPackages will not post a network read during
	// the lifetime of the protocol.
	//
	if (senddesc)
		client->writeNet();
	else
		continueSend();
}

void
ListPackages::netOut(int32 /*Amt*/)
{
	if (!done)
		continueSend();
	else
		client->protocolDone();
}

void
ListPackages::continueSend()
{
	PackageList* list;
	PackageDescription* item;

	list = PatchServer::This->getPackageList();
	if (!list)
	{
		done = true;
		client->errmsg(EM_PkgListChanged);	// ends this protocol
		return;
	}

    //check to see if the package list has been changed since we started sending
    if (PatchServer::This->getPkgListChangeCount() != m_pkgListChangeCount)
	{
		done = true;
		client->errmsg(EM_PkgListChanged);	// ends this protocol
		return;
	}

	item = list->getItem(tosend);

	if (tosend == 0)	// header msg for the whole list
	{
		// msg header
		//
		client->msgStart();
		client->msgAdd((uint8)'S');
		client->msgAdd((uint8)0x01);	// pkg list protocol version
		if (client->getMsgFormat() == Client::DOUBLE_BYTE)
			client->msgAdd((uint8)(list->getCount() & 0xff));
		else
			client->msgAdd((uint8)1);
		client->msgEnd();
	}

	if (client->getMsgFormat() == Client::DOUBLE_BYTE)
	{

		while (item)
		{

			if (!item->send(client, m_multibyte))
				break;

			++tosend;
			item = item->getNext();
		}
	}
	else
	{

		while (item)
		{
			if (item->getName().caseCompare("Lobby") == 0)
			{
				item->send(client, m_multibyte);

				item = 0;
				break;
			}

			item = item->getNext();
		}
	}

	done = (bool8)(item == (PackageDescription*)0);

	PatchServer::This->releasePackageList();

	client->writeNet();
}
